/*
 * Kmeans.cpp
 *
 *  Created on: 17/11/2013
 *      Author: juanignacio
 */

#include "Kmeans.h"
#include "stdlib.h"
#include "Operador.h"


Kmeans::Kmeans() {
	this->k = 0;
	this->dimension = 0;
	this->offset = new long int();
	this->eof = 0;
	this->unParser = Parser();
	this->lexico = new abb::ArbolB<Termino, 40>;
	this->vectoresCategorizados = new int;
}

Kmeans::Kmeans(int valor, int dimension){
	this->k = valor;
	this->dimension = dimension;
	this->offset = new long int[k];
	this->eof = 0;
	this->unParser = Parser();
	this->lexico = new abb::ArbolB<Termino, 40>;
	this->vectoresCategorizados = new int;
}

Kmeans::Kmeans(int k){
	this->k = k;
	this->dimension = 0;
	this->offset = new long int[k];
	this->eof = 0;
	this->unParser = Parser();
	this->lexico = new abb::ArbolB<Termino, 40>;
	this->vectoresCategorizados = new int;
}

Kmeans::~Kmeans() { }

void Kmeans::parsearDirectorio(char *unDirectorio){

	int *numDoc = new int;
	*numDoc = 0;
	float *offsetDocumentos;
	this->unParser.parsearDirectorio(unDirectorio, this->lexico);
	this->dimension = this->lexico->getEntradas();

	delete this->vectoresCategorizados;
	this->vectoresCategorizados = new int[this->unParser.getCantidadDocumentos()];
	for ( int j = 0; j < this->unParser.getCantidadDocumentos(); j++){
		vectoresCategorizados[j] = 0;
	}

	offsetDocumentos = this->lexico->guardarVectoresEnDisco(this->unParser.getCantidadDocumentos(), this->dimension);
	//Buscamos los vectores mas alejados para crear los centroides
	float *unDocumento = new float[this->dimension];
	float *otroDocumento = new float[this->dimension];
	this->getDocumentosMasAlejados(unDocumento, otroDocumento);
	this->crearCentroides(unDocumento, otroDocumento);

	/*---------------------------------------------------------------*/
	for (int m = 0; m < 15; m++){

	FILE *documentos = fopen("documentosCategorizados.dat", "w+");
	if (documentos == 0){
		cout<<"No se puede abrir el archivo seleccionado"<<endl;
		return;
	}
	for (int i = 0; i < this->unParser.getCantidadDocumentos(); i++){
		*numDoc = i;
		this->lexico->armarDocumento(*numDoc, unDocumento);
		//cout<<"DOCUMENTO "<<i<<"   ";
		this->asignarCluster(unDocumento, numDoc, documentos);
		/*if (m == 37 || m == 0){
		float *unCentroideParaImprimir = new float[this->lexico->getEntradas()];
		this->getCentroide(this->getOffset(9), unCentroideParaImprimir);
		for (int e = 1580; e < 1600; e++){
			cout<<unCentroideParaImprimir[e]<<", ";
		}
		cout<<endl;}*/

	}
	fclose(documentos);
	delete []this->vectoresCategorizados;
	reorganizarCentroides(this->k);
	}
	/*---------------------------------------------------------------*/

	delete numDoc;
	delete []unDocumento;
	delete []otroDocumento;

	return;
}

void Kmeans::getNombreDocumento(int unDocumento){

	cout<<"ENTRA A GET NOMBRE"<<endl;
	char *unNombre = new char[70];
	cout<<"pide memoria para el nombre?"<<endl;
	FILE *archivoNombres = fopen("nombreArchivos.txt", "r");
	for (int i = 0; i <= unDocumento; i++){
		fgets(unNombre, 100, archivoNombres);
	}
	fclose(archivoNombres);

	cout<<unNombre<<endl;

	delete []unNombre;

	return;
}

void Kmeans::emitirResultados(){

	FILE *documentos = fopen("documentosCategorizados.dat", "r");
	if (documentos == 0) {
		cout<<"No hay ningun documento para categorizar."<<endl;
		return;
	}

	FILE *archCantDocs = fopen("cantidadDocumentos.dat", "r");
	if (archCantDocs == 0) cout<<"No se pudo abrir el archivo de clusters"<<endl;

	int *cantDocumentos = new int; *cantDocumentos = 0;
	int *unK = new int; *unK = 0;
	int a = 0;
	fread(cantDocumentos, sizeof(cantDocumentos), 1, archCantDocs);
	fread(unK, sizeof(unK), 1, archCantDocs);
	fclose(archCantDocs);

	cout<<endl;
	cout<<endl;
	cout<<"Resultados obtenidos: "<<endl;
	cout<<endl;

	int *unDoc = new int; *unDoc = 0;
	//cout<<" -----------1-------------- "<<endl;
	int *primerC = new int; *primerC = 0;
	//cout<<" -----------2-------------- "<<endl;
	int *segundoC = new int; *segundoC = 0;
	//cout<<" -----------3-------------- "<<endl;

	for (int i = 0; i < *unK; i++){

		fseek(documentos, 0, 0);
		//cout<<" -----------4.1."<<i<<"-------------- "<<endl;
		for (int j = 0; j < *cantDocumentos; j++){
			if (fread(unDoc, sizeof(unDoc), 1, documentos) == 0) cout<<"ERROR"<<endl;
			//cout<<" -----------4.1."<<i<<"."<<j<<"-------------- "<<endl;
			if (fread(primerC, sizeof(primerC), 1, documentos) == 0) cout<<"ERROR"<<endl;;
			//cout<<" -----------4.1."<<i<<"."<<j<<"-------------- "<<endl;
			if (fread(segundoC, sizeof(segundoC), 1, documentos) == 0) cout<<"ERROR"<<endl;;
			//cout<<" -----------4.1."<<i<<"."<<j<<"-------------- "<<endl;
			//cout<<"DOC: "<<*unDoc<<" centroide: "<<*primerC<<endl;
			if (*primerC == i){
				a++;
				this->getNombreDocumento(*unDoc);
			}

		}
		if (a !=0 ){
			cout<<endl;
			cout<<endl;
			a = 0;
		}
	}

	delete unDoc;
	delete primerC;
	delete segundoC;
	delete unK;
	delete cantDocumentos;
	fclose(documentos);
	return;
}

void Kmeans::reorganizarCentroides(int k){

	float centroide[this->dimension];
	for (int i = 0; i < this->dimension; i++){
		centroide[i] = 0.0;
	}
	float *unDocumento = new float[this->dimension];
	FILE *centroides = fopen("centroides.dat", "r+");
	FILE *documentos = fopen("documentosCategorizados.dat", "r");
	if (documentos == 0) return;
	int *unDoc = new int;
	int *primerCercano = new int; *primerCercano = 0;
	int *segundoCercano = new int; *segundoCercano = 0;
	int documentosRestantes = this->unParser.getCantidadDocumentos();
	bool ninguno = true;
	int total = 0;

	for (int i = 0; i < this->unParser.getCantidadDocumentos(); i++)
		this->vectoresCategorizados[i] = 0;

	for (int j = 0; j < this->k; j++){
		fseek(documentos, 0, 0);
		for (int i = 0; i < this->unParser.getCantidadDocumentos(); i++){
			if (fread(unDoc, sizeof(int), 1, documentos) == 0) cout<<"read ERROR"<<endl; ;
			if (fread(primerCercano, sizeof(int), 1, documentos) == 0) cout<<"read ERROR"<<endl;;
			if (fread(segundoCercano, sizeof(int), 1, documentos) == 0) cout<<"read ERROR"<<endl;;
			if (*primerCercano == j){
				ninguno = false;
				total ++;
				this->lexico->armarDocumento(i, unDocumento);
				for (int k = 0; k < this->dimension; k++){
					centroide[k] = centroide[k] + unDocumento[k];
				}
			}

		}//end FOR i;
		//if (!ninguno){
			//cout<<"CHEQUEANDO SI ENTRA ACA ALGUNA VEZ..."<<endl;
		if (ninguno != 0){
			for (int k = 0; k < this->dimension; k++){
				centroide[k] = centroide[k]/total;
			}
		}
			total = 0;
			float *resultado = new float[this->dimension];
			this->obtenerVectorMasCercano(centroide, resultado);
			this->setCentroide(j, resultado);
			delete []resultado;
		//}

	}//end FOR j

	delete unDoc;
	delete primerCercano;
	delete segundoCercano;
	fclose(centroides);
	fclose(documentos);
	return;
}

void Kmeans::asignarCluster(float *unDocumento, int *numeroDoc, FILE *documentos){

	Operador unOperador;
	float *unCentroide = new float[this->dimension];

	int *unClusterMasCercano = new int;
	*unClusterMasCercano = 0;
	int *otroClusterMasCercano = new int;
	*otroClusterMasCercano = 0;

	float unaDistancia = 0.0;
	float otraDistancia = 0.0;
	float distanciaAux = 0.0;

	for (int i = 0; i < this->k; i++){
		//if (i == 1) cout<<"offset al centroide maximo: "<<this->getOffset(i)<<endl;
		this->getCentroide(this->getOffset(i), unCentroide);
		//if (i == 1){
		//	for (int m = 0; m < 30; m++) cout<<unCentroide[m]<<",";
		//}
		distanciaAux = unOperador.distanciaCoseno(unDocumento, unCentroide, this->dimension);
		//if (*numeroDoc == 17 && i == 1) cout<<"norma Vector: "<<unOperador.norma(unDocumento, this->dimension)<<endl;
		if (distanciaAux >= unaDistancia){
			unaDistancia = distanciaAux;
			//cout<<"Distancia a centroide "<<i<<": "<<unaDistancia<<endl;//////////////////////////////////////////BORRARRRRRRRRRRR
			*unClusterMasCercano = i;
		}else if (distanciaAux < unaDistancia && distanciaAux >= otraDistancia){
			otraDistancia = distanciaAux;
			*otroClusterMasCercano = i;
		}
	}

	fwrite(numeroDoc, sizeof(numeroDoc), 1, documentos);
	fwrite(unClusterMasCercano, sizeof(unClusterMasCercano), 1, documentos);
	fwrite(otroClusterMasCercano, sizeof(otroClusterMasCercano), 1, documentos);

	delete []unCentroide;
	delete unClusterMasCercano;
	delete otroClusterMasCercano;
	return;
}

void Kmeans::getDocumentosMasAlejados(float *unDocumento, float *otroDocumento){
	/*Al momento de ser llamado este metodo, ya tiene que estar parseado todo el directorio para asi tener
	 * ya formado el arbol de lexico*/

	float *docAux1 = new float[this->lexico->getEntradas()];
	float *docAux2 = new float[this->lexico->getEntradas()];
	int archivoMasAlejado1 = 0;
	int archivoMasAlejado2 = 0;

	float distanciaMax = 1.0;
	float aux = 1.0;
	Operador unOperador;

	for (int i = 0; i < this->unParser.getCantidadDocumentos()-1; i++){
		for (int j = i+1; j < this->unParser.getCantidadDocumentos(); j++){
			this->lexico->armarDocumento(i, docAux1);
			this->lexico->armarDocumento(j, docAux2);
			aux = unOperador.distanciaCoseno(docAux1, docAux2, this->lexico->getEntradas());
			if (aux <= distanciaMax){
				distanciaMax = aux;
				archivoMasAlejado1 = i;
				archivoMasAlejado2 = j;
				for (int k = 0; k < this->lexico->getEntradas(); k++){
					unDocumento[k] = docAux1[k];
					otroDocumento[k] = docAux2[k];
				}//end FOR
			}//end IF
		}//end FOR
	}//end FOR
	this->vectoresCategorizados[archivoMasAlejado1] = 1;
	//cout<<"Minimo: "<<archivoMasAlejado1<<endl;
	this->vectoresCategorizados[archivoMasAlejado2] = 1;
	//cout<<"Maximo: "<<archivoMasAlejado2<<endl;

	delete []docAux1;
	delete []docAux2;
	return;
}

void Kmeans::obtenerVectorMasCercano(float *unVector, float *unResultado){

	float distancia = 0.0; //cuanto mas grande la distancia coseno, mas semejantes son los vectores
	float distanciaAux = 0.0;
	int docMasProximo = 0;
	Operador unOperador;
	float *unDocAux = new float[this->dimension];

	for (int i = 0; i < this->unParser.getCantidadDocumentos(); i++){
		if (this->vectoresCategorizados[i] == 0){
			this->lexico->armarDocumento(i, unDocAux);
			distanciaAux = unOperador.distanciaCoseno(unVector, unDocAux, this->dimension);
			if (distanciaAux > distancia){
				distancia = distanciaAux;
				docMasProximo = i;
			}
		}
	}
	this->lexico->armarDocumento(docMasProximo, unResultado);
	this->vectoresCategorizados[docMasProximo] = 1;

	delete []unDocAux;
	return;
}

int Kmeans::crearCentroides(float *minimo, float *maximo){

	FILE *archivoCentroides = fopen("centroides.dat", "w+");
	Operador unOperador;
	float unCentroide[this->dimension];
	float *aux = new float[this->dimension];
	float *otroAux = new float[this->dimension];
	float *unDoc = new float[this->dimension];
	int i = 0; int a = 0;
	int *cantDocs = new int; *cantDocs = this->unParser.getCantidadDocumentos();
	int *unK = new int; *unK = this->k;

	if (this->unParser.getCantidadDocumentos() < this->k)
		this->k = this->unParser.getCantidadDocumentos();

	offset[i] = ftell(archivoCentroides);
	for (int k = 0; k < this->dimension; k++){
		unCentroide[k] = minimo[k];
	}//end FOR
	if (fwrite(unCentroide, sizeof(unCentroide), 1, archivoCentroides) == 0) cout<<"ERROR"<<endl;
	i++;

	if ( i != this->k){
		offset[i] = ftell(archivoCentroides);
		//cout<<"offset al centroide maximo: "<<offset[i]<<endl;
		for (int k = 0; k < this->dimension; k++){
			unCentroide[k] = maximo[k];
		}//end FOR
		if (fwrite(unCentroide, sizeof(unCentroide), 1, archivoCentroides) == 0) cout<<"ERROR"<<endl;;
		i++;
	}

	while (i != this->k){
		if ( a == 0){
			unOperador.puntoMedio(minimo, maximo, this->dimension, aux);
			offset[i] = ftell(archivoCentroides);
			this->obtenerVectorMasCercano(aux, otroAux);
			for (int k = 0; k < this->dimension; k++){
				unCentroide[k] = otroAux[k];
				minimo[k] = otroAux[k];
			}//end FOR
			if (fwrite(unCentroide, sizeof(unCentroide), 1, archivoCentroides) == 0) cout<<"ERROR"<<endl;;
			a = 1;
			i++;
		}
		if ( a == 1 && i != this->k){
			unOperador.puntoMedio(minimo, maximo, this->dimension, aux);
			offset[i] = ftell(archivoCentroides);
			this->obtenerVectorMasCercano(aux, otroAux);
			for (int k = 0; k < this->dimension; k++){
				unCentroide[k] = otroAux[k];
				maximo[k] = otroAux[k];
			}//end FOR
			if (fwrite(unCentroide, sizeof(unCentroide), 1, archivoCentroides) == 0) cout<<"ERROR"<<endl;;
			a = 2;
			i++;
		}
		if ( a == 2 && i != this->k){
			this->getCentroide(this->getOffset(0), maximo);
			unOperador.puntoMedio(minimo, maximo, this->dimension, aux);
			offset[i] = ftell(archivoCentroides);
			this->obtenerVectorMasCercano(aux, otroAux);
			for (int k = 0; k < this->dimension; k++){
				unCentroide[k] = otroAux[k];
				maximo[k] = otroAux[k];
			}//end FOR
			if (fwrite(unCentroide, sizeof(unCentroide), 1, archivoCentroides) == 0) cout<<"ERROR"<<endl;;
			a = 0;
			i++;
		}

	}
	this->eof = ftell(archivoCentroides);

	FILE *archivoCantidadDocumentos = fopen("cantidadDocumentos.dat", "w");
	fwrite(cantDocs, sizeof(cantDocs), 1, archivoCantidadDocumentos);
	fwrite(unK, sizeof(unK), 1, archivoCantidadDocumentos);
	fclose(archivoCantidadDocumentos);

	fclose(archivoCentroides);
	delete []aux;
	delete unK;
	delete cantDocs;
	delete []otroAux;
	return 0;
}

int Kmeans::crearCentroides(){

	FILE *archivoCentroides = fopen("centroides.dat", "w+");
	Operador unOperador;
	float unCentroide[this->dimension];
	int i = 0;

	offset[i] = ftell(archivoCentroides);
	for (int k = 0; k < this->dimension; k++){
		unCentroide[k] = 0.0;
	}//end FOR
	fwrite(unCentroide, sizeof(unCentroide), 1, archivoCentroides);
	i++;

	while (i != this->k){

		offset[i] = ftell(archivoCentroides);
		for (int j = 0; j < this->dimension; j++){
			unCentroide[j] = (float)(rand()%101);
			}//end FOR
		fwrite(unCentroide, sizeof(unCentroide), 1, archivoCentroides); //falta perfeccionar un poco el rango de valores inicial
		i++;
		}//end WHILE
	this->eof = ftell(archivoCentroides);
	fclose(archivoCentroides);
	return 0;
}

long int Kmeans::getOffset(int unCentroide){
	//unCentroide tiene que estar dentro del rango de valores permitido

	return this->offset[unCentroide];
}

void Kmeans::getCentroide(long int offset, float *unCentroide){
	//almacena el valor del centroide ubicado en la posicion offset del archivo en unCentroide
	float centroide[this->dimension];

	FILE *archivoCentroides = fopen("centroides.dat", "r+");
	fseek(archivoCentroides, offset, 0);

	fread(centroide, sizeof(centroide), 1, archivoCentroides);

	for (int i = 0; i < this->dimension; i++){
		unCentroide[i] = centroide[i];
		}
	fclose(archivoCentroides);
	return;
}

void Kmeans::getCentroideConRadio(long int offset, float *unCentroide, float *radio){
	//almacena el valor del centroide ubicado en la posicion offset del archivo en unCentroide
	float centroide[this->dimension];

	FILE *archivoCentroides = fopen("centroides.dat", "r+");
	fseek(archivoCentroides, offset, 0);
	fread(radio, sizeof(radio), 1, archivoCentroides);
	fread(centroide, sizeof(centroide), 1, archivoCentroides);

	for (int i = 0; i < this->dimension; i++){
		unCentroide[i] = centroide[i];
		}
	fclose(archivoCentroides);
	return;
}

int Kmeans::setCentroide(int numeroCentroide, float *unCentroide){

	FILE *archivoCentroides = fopen("centroides.dat", "r+");
	float centroide[this->dimension];
	for (int i = 0; i < this->dimension; i++){
		centroide[i] = unCentroide[i];
	}

	this->offset[numeroCentroide] = this->eof;
	fseek(archivoCentroides, this->eof, 0);
	fwrite(centroide, sizeof(centroide), 1, archivoCentroides);
	this->eof = ftell(archivoCentroides);

	fclose(archivoCentroides);
	return 0;
}

int Kmeans::setCentroideConRadio(int numeroCentroide, float *unCentroide, float *radio){

	FILE *archivoCentroides = fopen("centroides.dat", "r+");
	float centroide[this->dimension];
	for (int i = 0; i < this->dimension; i++){
		centroide[i] = unCentroide[i];
	}

	this->offset[numeroCentroide] = this->eof;
	fseek(archivoCentroides, this->eof, 0);
	fwrite(radio, sizeof(radio), 1, archivoCentroides);
	fwrite(centroide, sizeof(centroide), 1, archivoCentroides);
	this->eof = ftell(archivoCentroides);

	fclose(archivoCentroides);
	return 0;
}

